/*
	grhino.cc	GRhino Gnome Frontend
	Copyright (c) 2000-2005, 2006, 2010 Kriang Lerdsuwanakij
	email:		lerdsuwa@users.sourceforge.net

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
#define GNOME_DISABLE_DEPRECATED
#define GTK_DISABLE_DEPRECATED
*/

#include "config.h"

#include <gtk/gtk.h>
#include <gnome.h>

#include <pthread.h>
#include <locale.h>


#include <sys/types.h>
#include <time.h>
#include <sys/time.h>

#include <exception>
#include <stdexcept>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>
#include <deque>
#include <vector>

const
#include "black.xpm"
const
#include "white.xpm"
const
#include "empty.xpm"
const
#include "hint_black.xpm"
const
#include "hint_white.xpm"
const
#include "black_last.xpm"
const
#include "white_last.xpm"
const
#include "highlight_black.xpm"
const
#include "highlight_white.xpm"
const
#include "highlight_empty.xpm"

const
#include "top.xpm"
const
#include "bottom.xpm"
const
#include "left.xpm"
const
#include "right.xpm"

const
#include "icon_new.xpm"
const
#include "icon_empty.xpm"
const
#include "icon_black.xpm"
const
#include "icon_white.xpm"
const
#include "icon_bw.xpm"

#include "board.h"
#include "hash.h"
#include "alphabeta.h"
#include "opening.h"
#include "pattern.h"
#include "parity.h"
#include "book.h"
#include "game.h"
#include "gpref.h"
#include "gevalwin.h"
#include "ggamewin.h"
#include "ghistwin.h"
#include "gutil.h"
#include "randboard.h"
#include "load.h"

#include "gtstream.h"

// Grab version number in VERSION variable
#undef VERSION
const char *
#include "scripts/version"
;

#ifdef _
#undef _
#endif

#ifdef N_
#undef N_
#endif

#include <libintl.h>
#define _(x) gettext(x)
#define N_(x) x
#define ICONDIR1 "../share/pixmaps/"
#define LOCALEDIR1 "../share/locale/"

const char *prog_name = "GRhino";
const char *prog_ver = VERSION;

/*************************************************************************
	User interface variables
*************************************************************************/

GdkPixbuf *top_pixmap = 0, *bottom_pixmap = 0, *left_pixmap = 0, *right_pixmap = 0;
GdkPixbuf *black_pixmap = 0, *white_pixmap = 0, *empty_pixmap = 0;
GdkPixbuf *hint_black_pixmap = 0, *hint_white_pixmap = 0;
GdkPixbuf *black_last_pixmap = 0, *white_last_pixmap = 0;
GdkPixbuf *highlight_black_pixmap = 0, *highlight_white_pixmap = 0;
GdkPixbuf *highlight_empty_pixmap = 0;

int grid_width;
int grid_height;
int left_margin;
int top_margin;
int right_margin;
int bottom_margin;
GtkWidget *board_widget;

GtkWidget *status_widget;

GtkWidget *main_window;

// Toolbar
GtkWidget *main_toolbar, *edit_toolbar;
GtkWidget *button_new, *button_edit, *button_exit, *button_undo, *button_redo;
GtkWidget *button_begin, *button_prev, *button_next, *button_end;
GtkWidget *button_empty, *button_black, *button_white, *button_bw, *button_finish;

int icon_width;
int icon_height;
GtkWidget *icon_new, *icon_edit, *icon_exit, *icon_undo, *icon_redo;
GtkWidget *icon_begin, *icon_prev, *icon_next, *icon_end;
GtkWidget *icon_empty, *icon_black, *icon_white, *icon_bw, *icon_finish;

GtkTooltips* tooltips;

GdkCursor *view_cursor;

int main_x, main_y;

struct timeval start_time, stop_time;

int edit_board_color = BLACK;
bool button_drag_state = false;
int button_drag_from_x = 0, button_drag_from_y = 0;
int button_drag_to_x = 0, button_drag_to_y = 0;

/*************************************************************************
	Menu Info
*************************************************************************/

void menu_game_new(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_game_open(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_game_edit_board(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_game_undo(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_game_redo(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_game_exit(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_game_begin(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_game_prev(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_game_next(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_game_end(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_edit_empty(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_edit_black(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_edit_white(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_edit_player(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_edit_finish(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_view_toolbar(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_tools_evaluate_position(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_tools_game_history(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_settings_save(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_settings_preferences(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_settings_switch_color(GtkWidget * /*widget*/, gpointer /*data*/);
void menu_help_about(GtkWidget * /*widget*/, gpointer /*data*/);

#define MENU_INDEX_EDIT_BOARD	2
GnomeUIInfo menu_game_info[] = {
	GNOMEUIINFO_MENU_NEW_GAME_ITEM(menu_game_new, 0),
	GNOMEUIINFO_MENU_OPEN_ITEM(menu_game_open, 0),
	GNOMEUIINFO_ITEM_NONE(N_("_Edit board"), N_("Edit board and start game"), menu_game_edit_board),
	GNOMEUIINFO_SEPARATOR,
	GNOMEUIINFO_MENU_QUIT_ITEM(menu_game_exit, 0),
	GNOMEUIINFO_END
};

#define MENU_INDEX_UNDO		0
#define MENU_INDEX_REDO		1
#define MENU_INDEX_BEGIN	3
#define MENU_INDEX_PREV		4
#define MENU_INDEX_NEXT		5
#define MENU_INDEX_END		6
GnomeUIInfo menu_edit_info[] = {
	GNOMEUIINFO_MENU_UNDO_MOVE_ITEM(menu_game_undo, 0),
	GNOMEUIINFO_MENU_REDO_MOVE_ITEM(menu_game_redo, 0),
	GNOMEUIINFO_SEPARATOR,
	GNOMEUIINFO_ITEM_STOCK(N_("_Begin of game"), N_("Go to the game beginning"), menu_game_begin, GTK_STOCK_GOTO_TOP),
	GNOMEUIINFO_ITEM_STOCK(N_("_Previous move"), N_("Go to the previous move"), menu_game_prev, GTK_STOCK_GO_UP),
	GNOMEUIINFO_ITEM_STOCK(N_("_Next move"), N_("Go to the next move"), menu_game_next, GTK_STOCK_GO_DOWN),
	GNOMEUIINFO_ITEM_STOCK(N_("_Last move"), N_("Go to the last move"), menu_game_end, GTK_STOCK_GOTO_BOTTOM),
	GNOMEUIINFO_END
};

#define MENU_INDEX_SHOW_TOOLBAR		0
GnomeUIInfo menu_tools_info[] = {
	GNOMEUIINFO_TOGGLEITEM(N_("_Toolbar"), N_("Show/hide toolbar"), menu_view_toolbar, NULL),
	GNOMEUIINFO_SEPARATOR,
	GNOMEUIINFO_TOGGLEITEM(N_("_Pattern evaluation"), N_("Show/hide pattern evaluation score"), menu_tools_evaluate_position, NULL),
	GNOMEUIINFO_TOGGLEITEM(N_("Game _history"), N_("Show/hide game move history"), menu_tools_game_history, NULL),
	GNOMEUIINFO_END
};

#define MENU_INDEX_SWITCH_AI	0
GnomeUIInfo menu_settings_info[] = {
	GNOMEUIINFO_ITEM_NONE(N_("Switch _computer color"), NULL, menu_settings_switch_color),
	GNOMEUIINFO_SEPARATOR,
	GNOMEUIINFO_ITEM_NONE(N_("_Save Settings"), NULL, menu_settings_save),
	GNOMEUIINFO_MENU_PREFERENCES_ITEM(menu_settings_preferences, 0),
	GNOMEUIINFO_END
};

GnomeUIInfo menu_help_info[] = {
	GNOMEUIINFO_HELP("grhino"),
	GNOMEUIINFO_MENU_ABOUT_ITEM(menu_help_about, 0),
	GNOMEUIINFO_END
};

GnomeUIInfo menu_main_info[] = {
	GNOMEUIINFO_MENU_GAME_TREE(menu_game_info),
	GNOMEUIINFO_MENU_EDIT_TREE(menu_edit_info),
	GNOMEUIINFO_SUBTREE(N_("_View"), menu_tools_info),
	GNOMEUIINFO_MENU_SETTINGS_TREE(menu_settings_info),
	GNOMEUIINFO_MENU_HELP_TREE(menu_help_info),
	GNOMEUIINFO_END
};

/*************************************************************************
	View mode
*************************************************************************/

void set_view_mode(view_mode_type v)
{
			// Initialize cursor only once
	if (view_cursor == NULL && board_widget)
		view_cursor = gdk_cursor_new_for_display(
				gtk_widget_get_display(board_widget),
				GDK_X_CURSOR);

	if (v == VIEW_MODE_HISTORY)
		gdk_window_set_cursor(board_widget->window, view_cursor);
	else
		gdk_window_set_cursor(board_widget->window, NULL);
	view_mode = v;
}

/*************************************************************************
	Configuration file functions
*************************************************************************/

#define CFG_AI_PATH "GRhino/AI/"
#define CFG_BOARD_PATH "GRhino/Board/"
#define CFG_THEME_PATH "GRhino/Theme/"
#define CFG_VIEW_PATH "GRhino/View/"

void write_view_config()
{
	gnome_config_set_bool(CFG_VIEW_PATH "show_toolbar", show_toolbar);

	gnome_config_set_bool(CFG_VIEW_PATH "show_pattern_evaluation", show_pattern_evaluation);
	gnome_config_set_int(CFG_VIEW_PATH "pattern_evaluation_x", pattern_x);
	gnome_config_set_int(CFG_VIEW_PATH "pattern_evaluation_y", pattern_y);

	gnome_config_set_bool(CFG_VIEW_PATH "show_game_history", show_game_history);
	gnome_config_set_int(CFG_VIEW_PATH "game_history_width", gamehist_width);
	gnome_config_set_int(CFG_VIEW_PATH "game_history_height", gamehist_height);
	gnome_config_set_int(CFG_VIEW_PATH "game_history_x", gamehist_x);
	gnome_config_set_int(CFG_VIEW_PATH "game_history_y", gamehist_y);

	gnome_config_set_int(CFG_VIEW_PATH "game_list_width", gamelib_width);
	gnome_config_set_int(CFG_VIEW_PATH "game_list_height", gamelib_height);
	gnome_config_set_int(CFG_VIEW_PATH "game_list_x", gamelib_x);
	gnome_config_set_int(CFG_VIEW_PATH "game_list_y", gamelib_y);

	gnome_config_set_int(CFG_VIEW_PATH "main_window_x", main_x);
	gnome_config_set_int(CFG_VIEW_PATH "main_window_y", main_y);

	gnome_config_sync();
}

void write_config()
{
	gnome_config_set_int(CFG_AI_PATH "game_mode", static_cast<int>(get_game_mode()));

	if (computer_level == LEVEL_CUSTOM)
		gnome_config_set_int(CFG_AI_PATH "computer_level", LEVEL_CUSTOM_FILE);
	else
		gnome_config_set_int(CFG_AI_PATH "computer_level", computer_level);
	gnome_config_set_int(CFG_AI_PATH "midgame_depth", current_level_info.midgame_depth);
	gnome_config_set_int(CFG_AI_PATH "num_empty_winlossdraw", current_level_info.num_empty_winlossdraw);
	gnome_config_set_int(CFG_AI_PATH "num_empty_exact", current_level_info.num_empty_exact);

	gnome_config_set_int(CFG_AI_PATH "randomness", get_eval_random());
	gnome_config_set_int(CFG_AI_PATH "opening_var", opening_var);

	gnome_config_set_bool(CFG_BOARD_PATH "log_move", log_move);
	gnome_config_set_bool(CFG_BOARD_PATH "redo_ai_move", redo_ai_move);
	gnome_config_set_bool(CFG_BOARD_PATH "animate_opening", animate_opening);
	gnome_config_set_int(CFG_BOARD_PATH "animate_delay", animate_delay);
	gnome_config_set_int(CFG_BOARD_PATH "start_game_mode", static_cast<int>(start_game_mode));
	gnome_config_set_string(CFG_BOARD_PATH "opening", opening_name.c_str());
	gnome_config_set_int(CFG_BOARD_PATH "start_random_game_pieces", start_random_game_pieces);

						// This is in CFG_BOARD_PATH for
						// compatibility with older GRhino
	gnome_config_set_bool(CFG_BOARD_PATH "hint_move", hint_move);
	gnome_config_set_bool(CFG_THEME_PATH "show_last_move", show_last_move);
	gnome_config_set_bool(CFG_THEME_PATH "show_border", show_border);
	gnome_config_set_int(CFG_THEME_PATH "toolbar_icon_size", toolbar_icon_size);
	gnome_config_set_string(CFG_THEME_PATH "theme", theme_name.c_str());

						// Sync in side this call
	write_view_config();
}

void read_config()
{
	gboolean use_def;
						// Default to COMPUTER_WHITE
	int i = gnome_config_get_int_with_default(CFG_AI_PATH "game_mode=2", &use_def);
	if (i < 0 || i >= NUM_GAME_MODE)
		i = COMPUTER_WHITE;
	set_game_mode(static_cast<game_mode_type>(i));

	computer_level = gnome_config_get_int_with_default(CFG_AI_PATH "computer_level=1", &use_def);
	if (computer_level == LEVEL_CUSTOM)
		computer_level = LEVEL_CUSTOM;
	else if (computer_level < 0 || computer_level >= NUM_LEVEL_INFO+1)
		computer_level = 1;

	if (computer_level == LEVEL_CUSTOM) {
		current_level_info.midgame_depth = 
			gnome_config_get_int_with_default(CFG_AI_PATH "midgame_depth=6", &use_def);
		if (current_level_info.midgame_depth < 1 || current_level_info.midgame_depth > 20)
			current_level_info.midgame_depth = 6;

		current_level_info.num_empty_winlossdraw = 
			gnome_config_get_int_with_default(CFG_AI_PATH "num_empty_winlossdraw=12", &use_def);
		if (current_level_info.num_empty_winlossdraw < 1 || current_level_info.num_empty_winlossdraw > 20)
			current_level_info.num_empty_winlossdraw = 12;

		current_level_info.num_empty_exact = 
			gnome_config_get_int_with_default(CFG_AI_PATH "num_empty_exact=10", &use_def);
		if (current_level_info.num_empty_exact < 1 || current_level_info.num_empty_exact > 20)
			current_level_info.num_empty_exact = 10;
	}
	else {
		current_level_info.midgame_depth = level_info[computer_level].midgame_depth;
		current_level_info.num_empty_winlossdraw = level_info[computer_level].num_empty_winlossdraw;
		current_level_info.num_empty_exact = level_info[computer_level].num_empty_exact;
	}

	i = gnome_config_get_int_with_default(CFG_AI_PATH "randomness=0", &use_def);
	if (i < 1 || i > 10)
		i = 0;
	set_eval_random(i);

						// Default to Low
	opening_var = gnome_config_get_int_with_default(CFG_AI_PATH "opening_var=2", &use_def);
	if (opening_var < 0 || opening_var >= NUM_OPENING_VAR)
		opening_var = 1;
	set_book_random(opening_var);

	log_move = gnome_config_get_bool_with_default(CFG_BOARD_PATH "log_move=false", &use_def);
	redo_ai_move = gnome_config_get_bool_with_default(CFG_BOARD_PATH "redo_ai_move=true", &use_def);
	animate_opening = gnome_config_get_bool_with_default(CFG_BOARD_PATH "animate_opening=true", &use_def);

	animate_delay = gnome_config_get_int_with_default(CFG_BOARD_PATH "animate_delay=1", &use_def);
	if (animate_delay < 1 || animate_delay > NUM_ANIMATE_DELAY)
		animate_delay = 1;

	i = gnome_config_get_int_with_default(CFG_BOARD_PATH "start_game_mode=0", &use_def);
	if (i < 0 || i >= NUM_START_GAME_MODE)
		i = 0;
	start_game_mode = static_cast<start_game_mode_type>(i);

	char *str = gnome_config_get_string_with_default(CFG_BOARD_PATH "opening=None", &use_def);
	int id = get_opening_id(str);
	g_free(str);

	i = gnome_config_get_int_with_default(CFG_BOARD_PATH "start_random_game_pieces=20", &use_def);
	if (i < 4 || i > 32 || (i & 1))
		i = 20;
	start_random_game_pieces = i;

						// This is in CFG_BOARD_PATH for
						// compatibility with older GRhino
	hint_move = gnome_config_get_bool_with_default(CFG_BOARD_PATH "hint_move=false", &use_def);
	show_last_move = gnome_config_get_bool_with_default(CFG_THEME_PATH "show_last_move=false", &use_def);
	show_border = gnome_config_get_bool_with_default(CFG_THEME_PATH "show_border=false", &use_def);
	theme_name = gnome_config_get_string_with_default(CFG_THEME_PATH "theme=", &use_def);
	toolbar_icon_size = gnome_config_get_int_with_default(CFG_THEME_PATH "toolbar_icon_size=0", &use_def);
	if (toolbar_icon_size < 0 || toolbar_icon_size >= NUM_TOOLBAR_ICON_SIZE)
		toolbar_icon_size = 0;

	show_toolbar = gnome_config_get_bool_with_default(CFG_VIEW_PATH "show_toolbar=false", &use_def);

	show_pattern_evaluation = gnome_config_get_bool_with_default(CFG_VIEW_PATH "show_pattern_evaluation=false", &use_def);
	pattern_x = gnome_config_get_int_with_default(CFG_VIEW_PATH "pattern_evaluation_x=-1", &use_def);
	pattern_y = gnome_config_get_int_with_default(CFG_VIEW_PATH "pattern_evaluation_y=-1", &use_def);

	show_game_history = gnome_config_get_bool_with_default(CFG_VIEW_PATH "show_game_history=false", &use_def);
	gamehist_width = gnome_config_get_int_with_default(CFG_VIEW_PATH "game_history_width=120", &use_def);
	if (gamehist_width <= 0)
		gamehist_width = 120;
	gamehist_height = gnome_config_get_int_with_default(CFG_VIEW_PATH "game_history_height=300", &use_def);
	if (gamehist_height <= 0)
		gamehist_height = 300;
	gamehist_x = gnome_config_get_int_with_default(CFG_VIEW_PATH "game_history_x=-1", &use_def);
	gamehist_y = gnome_config_get_int_with_default(CFG_VIEW_PATH "game_history_y=-1", &use_def);

	gamelib_width = gnome_config_get_int_with_default(CFG_VIEW_PATH "game_list_width=120", &use_def);
	if (gamelib_width <= 0)
		gamelib_width = 120;
	gamelib_height = gnome_config_get_int_with_default(CFG_VIEW_PATH "game_list_height=300", &use_def);
	if (gamelib_height <= 0)
		gamelib_height = 300;
	gamelib_x = gnome_config_get_int_with_default(CFG_VIEW_PATH "game_list_x=-1", &use_def);
	gamelib_y = gnome_config_get_int_with_default(CFG_VIEW_PATH "game_list_y=-1", &use_def);

	main_x = gnome_config_get_int_with_default(CFG_VIEW_PATH "main_window_x=-1", &use_def);
	main_y = gnome_config_get_int_with_default(CFG_VIEW_PATH "main_window_y=-1", &use_def);

	opening_name = get_opening_name(id);
	get_opening_move_sequence(id, opening_move_queue);
}

/*************************************************************************
	Drawing functions
*************************************************************************/

void draw_board()
{
	int last_pos = -1;
	if (view_mode != VIEW_MODE_EDIT
	    && show_last_move
	    && (cur_game_info.num_history > cur_game_info.min_num_history)) {
		if (view_mode == VIEW_MODE_HISTORY) {
			if (view_position > 0)
				last_pos = cur_game_info.move_history[view_position-1];
		}
		else
			last_pos = cur_game_info.move_history[cur_game_info.num_history-2];
	}

	for (int x = 0; x < 8; ++x)
		for (int y = 0; y < 8; ++y) {
			GdkPixbuf *pixmap;
			int pos = xy_to_pos(x, y);
			if (view_mode == VIEW_MODE_EDIT) {
				bool x_ok = false, y_ok = false;
				if (button_drag_state
				    && button_drag_to_x != -1 && button_drag_to_y != -1) {
					if (button_drag_to_x >= button_drag_from_x) {
						if (x >= button_drag_from_x
						    && x <= button_drag_to_x)
							x_ok = true;
					}
					else {
						if (x >= button_drag_to_x
						    && x <= button_drag_from_x)
							x_ok = true;
					}
					if (button_drag_to_y >= button_drag_from_y) {
						if (y >= button_drag_from_y
						    && y <= button_drag_to_y)
							y_ok = true;
					}
					else {
						if (y >= button_drag_to_y
						    && y <= button_drag_from_y)
							y_ok = true;
					}
				}

				switch (view_board_ptr->board[pos]) {
					case BLACK:
						if (x_ok && y_ok)
							pixmap = highlight_black_pixmap;
						else
							pixmap = black_pixmap;
						break;
					case WHITE:
						if (x_ok && y_ok)
							pixmap = highlight_white_pixmap;
						else
							pixmap = white_pixmap;
						break;
					default:
						if (x_ok && y_ok)
							pixmap = highlight_empty_pixmap;
						else
							pixmap = empty_pixmap;
				}
			}
			else {
				switch (view_board_ptr->board[pos]) {
					case BLACK:
						if (pos == last_pos)
							pixmap = black_last_pixmap;
						else
							pixmap = black_pixmap;
						break;
					case WHITE:
						if (pos == last_pos)
							pixmap = white_last_pixmap;
						else
							pixmap = white_pixmap;
						break;
					default:
						if (hint_move
						    && view_board_ptr->can_play(cur_game_info.get_player(), pos)) {
							if (cur_game_info.get_player() == BLACK)
								pixmap = hint_black_pixmap;
							else
								pixmap = hint_white_pixmap;
						}
						else
							pixmap = empty_pixmap;
				}
			}

			gdk_draw_pixbuf(board_widget->window, 
					board_widget->style->fg_gc[GTK_STATE_NORMAL],
					pixmap,
					0, 0, x*grid_width+left_margin, y*grid_height+top_margin,
					grid_width, grid_height,
					GDK_RGB_DITHER_NORMAL, 0, 0);
		}

	if (show_border) {
		gdk_draw_pixbuf(board_widget->window, 
				board_widget->style->fg_gc[GTK_STATE_NORMAL],
				top_pixmap,
				0, 0, 0, 0,
				8*grid_width+left_margin+right_margin, top_margin,
				GDK_RGB_DITHER_NORMAL, 0, 0);
		gdk_draw_pixbuf(board_widget->window, 
				board_widget->style->fg_gc[GTK_STATE_NORMAL],
				bottom_pixmap,
				0, 0, 0, 8*grid_height+top_margin,
				8*grid_width+left_margin+right_margin, bottom_margin,
				GDK_RGB_DITHER_NORMAL, 0, 0);
		gdk_draw_pixbuf(board_widget->window, 
				board_widget->style->fg_gc[GTK_STATE_NORMAL],
				left_pixmap,
				0, 0, 0, top_margin,
				left_margin, 8*grid_height,
				GDK_RGB_DITHER_NORMAL, 0, 0);
		gdk_draw_pixbuf(board_widget->window, 
				board_widget->style->fg_gc[GTK_STATE_NORMAL],
				right_pixmap,
				0, 0, 8*grid_width+left_margin, top_margin,
				right_margin, 8*grid_height,
				GDK_RGB_DITHER_NORMAL, 0, 0);
	}

	gdk_flush();
}

void update_status_bar(update_state_type /*u*/, void *)
{
	gtstream buf;
	if (view_mode == VIEW_MODE_EDIT) {
		const char *player;
		switch (get_game_mode()) {
			case COMPUTER_RANDOM:
				player = _("random");
				break;
			case COMPUTER_ALTERNATE:
				player = (is_computer_player(view_player) ? _("human") : _("AI"));
				break;
			default:
				player = (is_computer_player(view_player) ? _("AI") : _("human"));
				break;
		}
		gtout(buf, _("%$ (%$)"))
		     << (view_player == BLACK ? _("Black's turn") : _("White's turn"))
		     << player;
	}
	else {
		if (cur_game_info.is_game_play()) {
			gtout(buf, _("%$ (%$)"))
			     << (cur_game_info.get_player() == BLACK ? _("Black's turn") : _("White's turn"))
			     << (get_wait_player() ? _("human") :
					(move_queue.size() ? _("Opening") : _("AI")));

			if (!cur_game_info.is_random_game()) {
				const char *s = find_opening_name();
				if (s && strcmp(s, "None"))
					gtout(buf, _("   [%$]")) << _(s);
			}
		}
		else {
			switch (cur_game_info.get_game_result()) {
				case game_info::game_result_end:
					{
						int	black_score = cur_game_info.board_ptr->board_black_score();
						int	white_score = cur_game_info.board_ptr->board_white_score();
						adjust_score(black_score, white_score);
						gtout(buf, _("%$ (%$-%$)"))
						     << (black_score == white_score ? _("Draw") :
							 (black_score > white_score ? _("Black wins") : _("White wins")))
						     << (black_score > white_score ? black_score : white_score)
						     << (black_score > white_score ? white_score : black_score);
					}
					break;
				case game_info::game_result_timeout_black:
					buf << _("White wins - Black ran out of time");
					break;
				case game_info::game_result_timeout_white:
					buf << _("Black wins - White ran out of time");
					break;
				case game_info::game_result_resign_black:
					buf << _("White wins - Black resigned");
					break;
				case game_info::game_result_resign_white:
					buf << _("Black wins - White resigned");
					break;
			}
		}

		if (view_mode == VIEW_MODE_HISTORY) {
			gtout(buf, _("  (%$ %$)"))
			     << "View move" << view_position;
		}
	}

	buf << std::ends;

	gnome_appbar_pop(GNOME_APPBAR(status_widget));
	gnome_appbar_push(GNOME_APPBAR(status_widget), buf.str().c_str());
}

void place_piece_and_update(int pos)
{
	bool	during_opening = false;

	if (move_queue.size()) {	// Move come from move queue
		move_queue.pop_front();	// Remove it
		during_opening = true;
	}

	cur_game_info.place_piece(pos, time_player);

	*view_board_ptr = *cur_game_info.board_ptr;
	set_view_mode(VIEW_MODE_NONE);

	time_player = 0;
	time_player_animate_delay = 0;

	if (cur_game_info.is_use_clock()) {		// Load clock
		clock_player = cur_game_info.get_clock();
	}

	if (!during_opening || animate_opening) {
		draw_board();
		update_move.update(UPDATE_FORWARD);
	}

	if (!cur_game_info.is_game_play())
		log_history("grhino.log", "player", "player");
}

void timeout_update()
{
	cur_game_info.player_timeout();

	update_move.update(UPDATE_FORWARD);
	if (!cur_game_info.is_game_play())
		log_history("grhino.log", "player", "player");
}

/*************************************************************************
	Computer AI
*************************************************************************/

gint		input_timeout_id;		// Lock required for access

void *computer_thread(void *)
{
	int last_state, last_type;
				// Set deferred cancellation to avoid
				// destroying hash table data structure
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &last_state);
	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &last_type);

	int pos = POS_INVALID;

	if (move_queue.size()) {
				// Remove entry from move_queue in
				// place_piece_and_update since move
				// can be canceled and resumed, say
				// when preferences are changed
		pos = move_queue.front();
	}
	else
		pos = get_computer_move();

				// Set immediate cancellation to avoid
				// getting stuck at the lock below
				// indefinitely
	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &last_type);

	pthread_mutex_lock(&input_lock);
	input_pos = pos;
	gettimeofday(&stop_time, 0);
	pthread_mutex_unlock(&input_lock);

	return NULL;
}

gint input_check(void *)
{
	pthread_mutex_lock(&input_lock);
	if (input_timeout_id == 0) {
					// Handle rare case
					// when multiple checks
					// are scheduled
		pthread_mutex_unlock(&input_lock);
		return TRUE;
	}

	int pos = input_pos;
	if (pos != POS_INVALID) {
		time_player_animate_delay++;
		if (time_player_animate_delay < animate_delay) {
					// Wait
			pthread_mutex_unlock(&input_lock);
		}
		else {
			gtk_timeout_remove(input_timeout_id);
			input_timeout_id = 0;

			if (ai_running) {
				void *status;
				pthread_join(ai_thread, &status);
				ai_running = false;
			}

			time_player = (stop_time.tv_sec - start_time.tv_sec) * 100
				      + (stop_time.tv_usec - start_time.tv_usec)/10000;

					// In case clock time out just
					// before the move
			if (cur_game_info.is_use_clock()) {
				clock_player -= time_player;
				if (clock_player < 0)
					timeout_update();
				else
					place_piece_and_update(pos);
			}
			else
				place_piece_and_update(pos);

				// Unlock here now after all states are
				// updated to prevent button click problem
			pthread_mutex_unlock(&input_lock);

				// Need to start running clock
			if (cur_game_info.is_game_play()) {
				if (get_wait_player())
					human_move();
				else
					computer_move();
			}
		}
	}
	else {
				// Check for clock time out
		if (cur_game_info.is_use_clock()) {
			struct timeval cur_time;
			gettimeofday(&cur_time, 0);
			int time_used = (cur_time.tv_sec - start_time.tv_sec) * 1000
					- (cur_time.tv_usec - start_time.tv_usec);

			if (clock_player - time_used < 0)  {
				gtk_timeout_remove(input_timeout_id);
				input_timeout_id = 0;

				if (ai_running) {
					pthread_cancel(ai_thread);
					void *status;
					pthread_join(ai_thread, &status);
					ai_running = false;
				}

				timeout_update();
			}
		}

		time_player_animate_delay++;
		pthread_mutex_unlock(&input_lock);
	}

	return TRUE;
}

void computer_move()
{
	pthread_mutex_lock(&input_lock);
	input_pos = POS_INVALID;
	gettimeofday(&start_time, 0);
	set_midgame_depth(current_level_info.midgame_depth);
				// Start computer thread
	pthread_create(&ai_thread, &ai_thread_attr, computer_thread, NULL);
	ai_running = true;
				// Repeatly check if computer is finished
	input_timeout_id = gtk_timeout_add(100, input_check, NULL);
	pthread_mutex_unlock(&input_lock);
}

void human_move()
{
	pthread_mutex_lock(&input_lock);
	input_pos = POS_INVALID;
	gettimeofday(&start_time, 0);
				// Repeatly check if computer is finished
	input_timeout_id = gtk_timeout_add(100, input_check, NULL);
	pthread_mutex_unlock(&input_lock);
}

void cancel_input()
{
				// Terminate AI thread 
	pthread_mutex_lock(&input_lock);
	if (input_timeout_id) {
		gtk_timeout_remove(input_timeout_id);
		input_timeout_id = 0;

		if (ai_running) {
			pthread_cancel(ai_thread);
			void *status;
			pthread_join(ai_thread, &status);
			ai_running = false;
		}
	}
	pthread_mutex_unlock(&input_lock);
}

/*************************************************************************
	Game operations
*************************************************************************/

void init_game_before_draw()
{
	read_config();
}

void update_menu_and_toolbar(update_state_type, void *);
void cancel_edit_mode();

void init_game()
{
	trans_table_init();
	set_max_hash_move(57);

	pattern_table_init();
	book_init();
	srand(time(NULL));

	pthread_attr_init(&ai_thread_attr);
//	pthread_attr_setdetachstate(&ai_thread_attr, PTHREAD_CREATE_DETACHED);

	update_move.add(update_status_bar, 0);
	update_move.add(update_menu_and_toolbar, 0);
}

void new_game()
{
	cancel_input();
	cancel_edit_mode();

	eval_new_game();

	new_game(cur_game_info);

	*view_board_ptr = *cur_game_info.board_ptr;
	set_view_mode(VIEW_MODE_NONE);

	draw_board();

	time_player = 0;
	time_player_animate_delay = 0;

				// Make sure history is setup so that
				// Undo/redo capability is properly
				// computed.
	update_move.update(UPDATE_ALL);

				// If there is any Game List window
				// opened, remove the highlight on the
				// window.
	update_game_list.update(UPDATE_ALL);

	ai_tampered = false;
	ai_running = false;

	if (cur_game_info.is_game_play()) {
		if (get_wait_player())
			human_move();
		else {
			if (move_queue.size() && !animate_opening) {
					// Place all the moves immediately
				do {
					int pos = move_queue.front();
					place_piece_and_update(pos);
				} while (move_queue.size());
					// Draw board only once after all moves
					// are placed
				draw_board();
				update_move.update(UPDATE_FORWARD);
					// game_play is updated in
					// place_piece_and_update call above
					// Need to start running clock
				if (cur_game_info.is_game_play()) {
					if (get_wait_player())
						human_move();
					else
						computer_move();
				}
			}
			else
				computer_move();
		}
	}
}

void open_game()
{
	GtkWidget *file_selector = gtk_file_selection_new(_("Select a file to open"));
	gtk_widget_show(file_selector);
	gtk_window_set_modal(GTK_WINDOW(file_selector), TRUE);
	gtk_window_set_transient_for(GTK_WINDOW(file_selector), GTK_WINDOW(main_window));
	gint result = gtk_dialog_run(GTK_DIALOG(file_selector));

	std::string filename;
	switch (result) {
		case GTK_RESPONSE_OK:
			// FIXME: Check if it is a file
			filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION(file_selector));
			break;
	}
	gtk_widget_destroy(file_selector);

	if (filename.size())
		load_game_list(filename.c_str());
}

void undo()
{
	cancel_input();

	cur_game_info.undo();

	*view_board_ptr = *cur_game_info.board_ptr;
	set_view_mode(VIEW_MODE_NONE);

	time_player = 0;
	time_player_animate_delay = 0;
	clock_player = cur_game_info.get_clock();

	draw_board();
	update_move.update(UPDATE_BACKWARD);

				// Need to start running clock
	if (cur_game_info.is_game_play()) {
		if (get_wait_player())
			human_move();
		else
			computer_move();
	}
}

void redo()
{
	cancel_input();

	cur_game_info.redo();

	*view_board_ptr = *cur_game_info.board_ptr;
	set_view_mode(VIEW_MODE_NONE);

	time_player = 0;
	time_player_animate_delay = 0;
	clock_player = cur_game_info.get_clock();

	draw_board();
	update_move.update(UPDATE_FORWARD);

				// Need to start running clock
	if (cur_game_info.is_game_play()) {
		if (get_wait_player())
			human_move();
		else
			computer_move();
	}
}

void switch_computer_color()
{
	cancel_input();

	maybe_set_ai_tampered();

	switch_computer_game_mode();

	update_move.update(UPDATE_NONE);

				// Need to start running clock
	if (view_mode != VIEW_MODE_EDIT
	    && cur_game_info.is_game_play()) {
		if (get_wait_player())
			human_move();
		else
			computer_move();
	}
}

/*************************************************************************
	Edit board
*************************************************************************/

void update_toolbar();

void cancel_edit_mode()
{
	if (view_mode != VIEW_MODE_EDIT)
		return;

				// Enable edit menu
	gtk_widget_set_sensitive(menu_game_info[MENU_INDEX_EDIT_BOARD].widget,
				 TRUE);

			// No longer highlight drag
	button_drag_state = false;

	gtk_widget_set_sensitive(button_edit, TRUE);

	set_view_mode(VIEW_MODE_NONE);
	update_toolbar();	// Hide edit toolbar if necessary

			// Later the board should be redrawn by caller
}

void finish_edit_mode()
{
	int empties = 0;
	for (int i = 0; i < 64; ++i) {
		if (view_board_ptr->is_empty(i))
			++empties;
	}
	if (empties > 60) {
				// Many data structures only have
				// space for 60 moves maximum
		gtstream bufstr;
		bufstr << _("There must be at least 4 pieces on the board.");
		error_message_box(bufstr);
		return;
	}

	if (view_board_ptr->is_empty(3, 3)
	    || view_board_ptr->is_empty(3, 4)
	    || view_board_ptr->is_empty(4, 3)
	    || view_board_ptr->is_empty(4, 4)) {
				// This will confuse AI pattern evaluation
		gtstream bufstr;
		bufstr << _("The center 4 pieces must be filled.");
		error_message_box(bufstr);
		return;
	}

	cancel_edit_mode();

	eval_new_game();

	new_game(cur_game_info, *view_board_ptr, view_player);

	draw_board();

	time_player = 0;
	time_player_animate_delay = 0;

				// Make sure history is setup so that
				// Undo/redo capability is properly
				// computed.
	update_move.update(UPDATE_ALL);

	ai_tampered = false;
	ai_running = false;

	if (cur_game_info.is_game_play()) {
		if (get_wait_player())
			human_move();
		else
			computer_move();
	}
}

void edit_board()
{
				// Already editing board
	if (view_mode == VIEW_MODE_EDIT)
		return;

	button_drag_state = false;
	cancel_input();


	eval_new_game();

				// Take current player & board
	if (view_mode == VIEW_MODE_HISTORY)
		view_player = cur_game_info.player_history[view_position];
	else
		view_player = cur_game_info.get_player();
	byte_board_info b(*view_board_ptr);
	new_game(cur_game_info, b, view_player);

				// Color to fill
	edit_board_color = BLACK;

				// Disable edit menu
	gtk_widget_set_sensitive(menu_game_info[MENU_INDEX_EDIT_BOARD].widget,
				 FALSE);

	gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(button_black),
					  TRUE);

				// Enter view mode
	set_view_mode(VIEW_MODE_EDIT);

	gtk_widget_set_sensitive(button_edit, FALSE);
	update_toolbar();	// Show edit toolbar if necessary

	draw_board();

				// Make sure history is setup so that
				// Undo/redo capability is properly
				// computed.
	update_move.update(UPDATE_ALL);

				// If there is any Game List window
				// opened, remove the highlight on the
				// window.
	update_game_list.update(UPDATE_ALL);
}

/*************************************************************************
	Signals
*************************************************************************/

void destroy_signal(GtkWidget *window, gpointer /*data*/)
{
	gtk_window_get_position(GTK_WINDOW(window), &main_x, &main_y);
	write_view_config();
	gtk_main_quit();
}

void expose_signal(GtkWidget * /*window*/, GdkEventExpose * /*event*/, gpointer /*data*/)
{
	draw_board();
}

void button_press_signal(GtkWidget * /*window*/, GdkEventButton *event, gpointer /*data*/)
{
	if (view_mode == VIEW_MODE_NONE
	    && get_wait_player() && event->type == GDK_BUTTON_PRESS
	    && event->button == 1
	    && event->x >= left_margin && event->x < 8*grid_width+left_margin
	    && event->y >= top_margin && event->y < 8*grid_height+top_margin) {

		int x = static_cast<int>((event->x - left_margin)/ grid_width);
		int y = static_cast<int>((event->y - top_margin)/ grid_height);
		int pos = xy_to_pos(x, y);

		if (cur_game_info.board_ptr->can_play(cur_game_info.get_player(), pos)) {
			pthread_mutex_lock(&input_lock);
			input_pos = pos;
			gettimeofday(&stop_time, 0);
			pthread_mutex_unlock(&input_lock);
		}
	}
	else if (view_mode == VIEW_MODE_EDIT) {
		if (event->type == GDK_BUTTON_PRESS
		    && event->button == 1
		    && event->x >= left_margin && event->x < 8*grid_width+left_margin
		    && event->y >= top_margin && event->y < 8*grid_height+top_margin) {
			button_drag_state = true;
			button_drag_from_x = static_cast<int>((event->x - left_margin)/ grid_width);
			button_drag_from_y = static_cast<int>((event->y - top_margin)/ grid_height);
			button_drag_to_x = button_drag_from_x;
			button_drag_to_y = button_drag_from_y;
			// Intentionally not calling draw_board to avoid
			// highlighting square in a simple mouse click
		}
		else if (event->type == GDK_BUTTON_RELEASE
			 && event->button == 1
			 && button_drag_state == true) {
			if (event->x >= left_margin && event->x < 8*grid_width+left_margin
			    && event->y >= top_margin && event->y < 8*grid_height+top_margin) {
				int to_x = static_cast<int>((event->x - left_margin)/ grid_width);
				int to_y = static_cast<int>((event->y - top_margin)/ grid_height);

				if (to_x < button_drag_from_x) {
					int temp = to_x;
					to_x = button_drag_from_x;
					button_drag_from_x = temp;
				}
				if (to_y < button_drag_from_y) {
					int temp = to_y;
					to_y = button_drag_from_y;
					button_drag_from_y = temp;
				}

				for (int x = button_drag_from_x; x <= to_x; ++x) {
					for (int y = button_drag_from_y; y <= to_y; ++y) {
						int pos = xy_to_pos(x, y);
						view_board_ptr->set_pos(edit_board_color, pos);
					}
				}
				button_drag_state = false;
				draw_board();
				update_move.update(UPDATE_ALL);
			}
			else
				button_drag_state = false;
		}
		else if (event->type == GDK_BUTTON_PRESS
			 && event->button == 3) {
			switch (edit_board_color) {
				case BLACK:
					edit_board_color = WHITE;
					gtk_toggle_tool_button_set_active
					  (GTK_TOGGLE_TOOL_BUTTON(button_white),
					   TRUE);
					break;
				case WHITE:
					edit_board_color = EMPTY;
					gtk_toggle_tool_button_set_active
					  (GTK_TOGGLE_TOOL_BUTTON(button_empty),
					   TRUE);
					break;
				case EMPTY:
					edit_board_color = BLACK;
					gtk_toggle_tool_button_set_active
					  (GTK_TOGGLE_TOOL_BUTTON(button_black),
					   TRUE);
					break;
			}
		}
		else if (event->type == GDK_BUTTON_PRESS
			 && event->button == 2) {
			menu_edit_player(NULL, NULL);
		}
	}
}

gboolean mouse_move_signal(GtkWidget * /*window*/, GdkEventButton *event, gpointer /*data*/)
{
	if (view_mode == VIEW_MODE_EDIT && button_drag_state) {
		int x = -1;
		int y = -1;
		if (event->x >= left_margin && event->x < 8*grid_width+left_margin
		    && event->y >= top_margin && event->y < 8*grid_height+top_margin) {
			x = static_cast<int>((event->x - left_margin)/ grid_width);
			y = static_cast<int>((event->y - top_margin)/ grid_height);
		}

		if (x != button_drag_to_x || y != button_drag_to_y) {
			button_drag_to_x = x;
			button_drag_to_y = y;
			draw_board();
			update_move.update(UPDATE_ALL);
		}
	}
	return TRUE;
}

/*************************************************************************
	Adjust toolbar according to current settings
*************************************************************************/

bool main_toolbar_docked = false;
bool edit_toolbar_docked = false;

void update_toolbar()
{
        if (show_toolbar && !main_toolbar_docked) {
	        gnome_app_add_toolbar(GNOME_APP(main_window),
		  	              GTK_TOOLBAR(main_toolbar),
                                      "main_toolbar",
                                      BONOBO_DOCK_ITEM_BEH_EXCLUSIVE,
                                      BONOBO_DOCK_TOP,
                                      1, 0, 0);
                main_toolbar_docked = true;
        }
	if (main_toolbar_docked) {
  		BonoboDockItem *dock_item = gnome_app_get_dock_item_by_name(GNOME_APP(main_window),
									            "main_toolbar");
                if (show_toolbar)
			gtk_widget_show(GTK_WIDGET(dock_item));
                else
		        gtk_widget_hide(GTK_WIDGET(dock_item));
        }

	if (view_mode == VIEW_MODE_EDIT && !edit_toolbar_docked) {
		gnome_app_add_toolbar(GNOME_APP(main_window),
				      GTK_TOOLBAR(edit_toolbar),
				      "edit_toolbar",
				      BONOBO_DOCK_ITEM_BEH_EXCLUSIVE,
				      BONOBO_DOCK_TOP,
				      2, 0, 0);
		edit_toolbar_docked = true;
	}
	if (edit_toolbar_docked) {
		BonoboDockItem *dock_item = gnome_app_get_dock_item_by_name(GNOME_APP(main_window),
								    "edit_toolbar");
		if (view_mode == VIEW_MODE_EDIT)
			gtk_widget_show(GTK_WIDGET(dock_item));
		else
			gtk_widget_hide(GTK_WIDGET(dock_item));
	}

	gtk_window_resize(GTK_WINDOW(main_window), 1, 1);
}

/*************************************************************************
	Menu callback functions
*************************************************************************/

void menu_game_new(GtkWidget * /*widget*/, gpointer /*data*/)
{
	new_game();
}

void menu_game_open(GtkWidget * /*widget*/, gpointer /*data*/)
{
	open_game();
}

void menu_game_edit_board(GtkWidget * /*widget*/, gpointer /*data*/)
{
	edit_board();
}

void menu_game_undo(GtkWidget * /*widget*/, gpointer /*data*/)
{
	undo();
}

void menu_game_redo(GtkWidget * /*widget*/, gpointer /*data*/)
{
	redo();
}

void menu_game_exit(GtkWidget * /*widget*/, gpointer /*data*/)
{
	gtk_main_quit();
}

void menu_game_begin(GtkWidget * /*widget*/, gpointer /*data*/)
{
	*view_board_ptr = cur_game_info.board_history[0];
	view_player = cur_game_info.player_history[0];
	set_view_mode(VIEW_MODE_HISTORY);
	view_position = 0;
	draw_board();
	update_move.update(UPDATE_NONE);
}

void menu_game_prev(GtkWidget * /*widget*/, gpointer /*data*/)
{
	if (view_mode == VIEW_MODE_NONE) {
		set_view_mode(VIEW_MODE_HISTORY);
		view_position = cur_game_info.num_history-2;
	}
	else
		--view_position;
	*view_board_ptr = cur_game_info.board_history[view_position];
	view_player = cur_game_info.player_history[view_position];
	draw_board();
	update_move.update(UPDATE_NONE);
}

void menu_game_next(GtkWidget * /*widget*/, gpointer /*data*/)
{
	++view_position;
	if (view_position == cur_game_info.num_history-1)
		set_view_mode(VIEW_MODE_NONE);
	*view_board_ptr = cur_game_info.board_history[view_position];
	view_player = cur_game_info.player_history[view_position];
	draw_board();
	update_move.update(UPDATE_NONE);
}

void menu_game_end(GtkWidget * /*widget*/, gpointer /*data*/)
{
	*view_board_ptr = *cur_game_info.board_ptr;
	set_view_mode(VIEW_MODE_NONE);
	draw_board();
	update_move.update(UPDATE_NONE);
}

void menu_edit_empty(GtkWidget * /*widget*/, gpointer /*data*/)
{
	edit_board_color = EMPTY;
}

void menu_edit_black(GtkWidget * /*widget*/, gpointer /*data*/)
{
	edit_board_color = BLACK;
}

void menu_edit_white(GtkWidget * /*widget*/, gpointer /*data*/)
{
	edit_board_color = WHITE;
}

void menu_edit_player(GtkWidget * /*widget*/, gpointer /*data*/)
{
	view_player = switch_color(view_player);

				// Update status bar to reflect
				// new player
	update_move.update(UPDATE_ALL);
}

void menu_edit_finish(GtkWidget * /*widget*/, gpointer /*data*/)
{
	finish_edit_mode();
}

void menu_settings_save(GtkWidget * /*widget*/, gpointer /*data*/)
{
	write_config();
}

void menu_settings_preferences(GtkWidget * /*widget*/, gpointer /*data*/)
{
	preferences();
}

void menu_settings_switch_color(GtkWidget * /*widget*/, gpointer /*data*/)
{
	switch_computer_color();
}

void menu_help_about(GtkWidget * /*widget*/, gpointer /*data*/)
{
	static GtkWidget *about_dialog = NULL;
	if (!about_dialog) {
		static const gchar *authors[2];
		authors[0] = _("Kriang Lerdsuwanakij");
		authors[1] = 0;
		static const gchar *documenters[2];
		documenters[0] = _("Kriang Lerdsuwanakij");
		documenters[1] = 0;
		gchar *translator = _("TRANSLATOR");
		if (strcmp (translator, "TRANSLATOR") == 0)
			translator = 0;

		about_dialog = gnome_about_new(_(prog_name), prog_ver,
					       _("(c) 2000-7, 2010 Kriang Lerdsuwanakij"),
					       _("A reversi program"),
					       authors,
					       documenters,
					       translator,
					       0);
		g_signal_connect(G_OBJECT(about_dialog), "destroy",
				 G_CALLBACK(gtk_widget_destroyed),
				 &about_dialog);
	}
	gtk_window_present(GTK_WINDOW(about_dialog));
}

/*************************************************************************
	Pixmap function
*************************************************************************/

void default_pixmap_error(const char *str)
{
	gtstream bufstr;
	gtout(bufstr, _("size of theme pixmap %$ does not agree with others"))
		<< str;
	throw std::range_error(bufstr.str());
}

GdkPixbuf *load_pixmap(const char *basename, const char *default_xpm[], 
		       bool use_default)
{
	if (use_default)
		return gdk_pixbuf_new_from_xpm_data(default_xpm);
	else {
		std::string pathname = THEMEDIR;
		pathname.append(theme_name);
		pathname.append("/");
		pathname.append(basename);
		std::string filename = pathname;
		filename.append(".png");
		if (!g_file_test(filename.c_str(), G_FILE_TEST_EXISTS)) {
			filename = pathname;
			filename.append(".xpm");
			if (!g_file_test(filename.c_str(), G_FILE_TEST_EXISTS)) {
				gtstream bufstr;
				gtout(bufstr, _("pixmap %$ missing"))
					<< basename;
				throw std::runtime_error(bufstr.str());
			}
		}
		GdkPixbuf *pixmap = gdk_pixbuf_new_from_file(filename.c_str(), 
							     NULL);
		if (!pixmap) {
			gtstream bufstr;
			gtout(bufstr, _("pixmap %$ contains error"))
				<< filename;
			throw std::runtime_error(bufstr.str());
		}
		return pixmap;
	}
}

void load_icon_image(GtkWidget *widget, const char *basename, 
		     const char *stock_name, 
		     bool use_default)
{
	if (use_default) {
		gtk_image_set_from_stock(GTK_IMAGE(widget), stock_name, 
					 toolbar_icon_map[toolbar_icon_size]);
		return;
	}
	else {
		std::string pathname = THEMEDIR;
		pathname.append(theme_name);
		pathname.append("/");
		pathname.append(basename);
		std::string filename = pathname;
		filename.append(".png");
		if (!g_file_test(filename.c_str(), G_FILE_TEST_EXISTS)) {
			filename = pathname;
			filename.append(".xpm");
			if (!g_file_test(filename.c_str(), G_FILE_TEST_EXISTS)) {
				gtk_image_set_from_stock(GTK_IMAGE(widget), 
							 stock_name, 
							 toolbar_icon_map[toolbar_icon_size]);
				return;
			}
		}
		GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename.c_str(), 
							     NULL);
		if (!pixbuf) {
			gtk_image_set_from_stock(GTK_IMAGE(widget), 
						 stock_name, 
						 toolbar_icon_map[toolbar_icon_size]);
			return;
		}
		GdkPixbuf *pixbuf2 = gdk_pixbuf_scale_simple(pixbuf, 
							     icon_width, 
							     icon_height, 
							     GDK_INTERP_BILINEAR);
		gtk_image_set_from_pixbuf(GTK_IMAGE(widget), pixbuf2);
		g_object_unref(pixbuf);
		g_object_unref(pixbuf2);
		return;
	}
}

void load_icon_image(GtkWidget *widget, const char *basename, 
		     const char *default_xpm[], 
		     bool use_default)
{
	if (use_default) {
		GdkPixbuf *p = scale_pixbuf_from_xpm_data(default_xpm, 
							  icon_width, 
							  icon_height);
		gtk_image_set_from_pixbuf(GTK_IMAGE(widget), p);
		g_object_unref(p);
		return;
	}
	else {
		std::string pathname = THEMEDIR;
		pathname.append(theme_name);
		pathname.append("/");
		pathname.append(basename);
		std::string filename = pathname;
		filename.append(".png");
		if (!g_file_test(filename.c_str(), G_FILE_TEST_EXISTS)) {
			filename = pathname;
			filename.append(".xpm");
			if (!g_file_test(filename.c_str(), G_FILE_TEST_EXISTS)) {
				GdkPixbuf *p = scale_pixbuf_from_xpm_data(default_xpm, 
									  icon_width, 
									  icon_height);
				gtk_image_set_from_pixbuf(GTK_IMAGE(widget), p);
				g_object_unref(p);
				return;
			}
		}
		GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename.c_str(), NULL);
		if (!pixbuf) {
			GdkPixbuf *p = scale_pixbuf_from_xpm_data(default_xpm, 
								  icon_width, 
								  icon_height);
			gtk_image_set_from_pixbuf(GTK_IMAGE(widget), p);
			g_object_unref(p);
			return;
		}
		GdkPixbuf *pixbuf2 = gdk_pixbuf_scale_simple(pixbuf, icon_width, 
							     icon_height, 
							     GDK_INTERP_BILINEAR);
		gtk_image_set_from_pixbuf(GTK_IMAGE(widget), pixbuf2);
		g_object_unref(pixbuf);
		g_object_unref(pixbuf2);
		return;
	}
}

void load_pixmaps(bool use_default)
{
					// Free memory used by old pixmaps
	if (black_pixmap) {
		g_object_unref(black_pixmap);
		black_pixmap = 0;
	}
	if (white_pixmap) {
		g_object_unref(white_pixmap);
		white_pixmap = 0;
	}
	if (empty_pixmap) {
		g_object_unref(empty_pixmap);
		empty_pixmap = 0;
	}
	if (hint_black_pixmap) {
		g_object_unref(hint_black_pixmap);
		hint_black_pixmap = 0;
	}
	if (hint_white_pixmap) {
		g_object_unref(hint_white_pixmap);
		hint_white_pixmap = 0;
	}
	if (black_last_pixmap) {
		g_object_unref(black_last_pixmap);
		black_last_pixmap = 0;
	}
	if (white_last_pixmap) {
		g_object_unref(white_last_pixmap);
		white_last_pixmap = 0;
	}
	if (highlight_black_pixmap) {
		g_object_unref(highlight_black_pixmap);
		highlight_black_pixmap = 0;
	}
	if (highlight_white_pixmap) {
		g_object_unref(highlight_white_pixmap);
		highlight_white_pixmap = 0;
	}
	if (highlight_empty_pixmap) {
		g_object_unref(highlight_empty_pixmap);
		empty_pixmap = 0;
	}
	if (left_pixmap) {
		g_object_unref(left_pixmap);
		left_pixmap = 0;
	}
	if (right_pixmap) {
		g_object_unref(right_pixmap);
		right_pixmap = 0;
	}
	if (top_pixmap) {
		g_object_unref(top_pixmap);
		top_pixmap = 0;
	}
	if (bottom_pixmap) {
		g_object_unref(bottom_pixmap);
		bottom_pixmap = 0;
	}

	black_pixmap = load_pixmap("black", const_cast<const char**>(black_xpm), 
				   use_default);
	grid_width = gdk_pixbuf_get_width(black_pixmap);
	grid_height = gdk_pixbuf_get_height(black_pixmap);

	white_pixmap = load_pixmap("white", const_cast<const char**>(white_xpm), 
				   use_default);
	if (gdk_pixbuf_get_width(white_pixmap) != grid_width
	    || gdk_pixbuf_get_height(white_pixmap) != grid_height)
		default_pixmap_error("white");
	
	empty_pixmap = load_pixmap("empty", const_cast<const char**>(empty_xpm), 
				   use_default);
	if (gdk_pixbuf_get_width(empty_pixmap) != grid_width
	    || gdk_pixbuf_get_height(empty_pixmap) != grid_height)
		default_pixmap_error("empty");

	highlight_black_pixmap = load_pixmap("highlight_black", 
					const_cast<const char**>(highlight_black_xpm), 
					use_default);
	if (gdk_pixbuf_get_width(highlight_black_pixmap) != grid_width
	    || gdk_pixbuf_get_height(highlight_black_pixmap) != grid_height)
		default_pixmap_error("highlight_black");
	
	highlight_white_pixmap = load_pixmap("highlight_white",
					const_cast<const char**>(highlight_white_xpm), 
					use_default);
	if (gdk_pixbuf_get_width(highlight_white_pixmap) != grid_width
	    || gdk_pixbuf_get_height(highlight_white_pixmap) != grid_height)
		default_pixmap_error("highlight_white");
	
	highlight_empty_pixmap = load_pixmap("highlight_empty",
					const_cast<const char**>(highlight_empty_xpm), 
					use_default);
	if (gdk_pixbuf_get_width(highlight_empty_pixmap) != grid_width
	    || gdk_pixbuf_get_height(highlight_empty_pixmap) != grid_height)
		default_pixmap_error("highlight_empty");

	if (hint_move) {
		hint_black_pixmap = load_pixmap("hint_black", const_cast<const char**>(hint_black_xpm), 
						use_default);
		if (gdk_pixbuf_get_width(hint_black_pixmap) != grid_width
		    || gdk_pixbuf_get_height(hint_black_pixmap) != grid_height)
			default_pixmap_error("hint_black");

		hint_white_pixmap = load_pixmap("hint_white", const_cast<const char**>(hint_white_xpm), 
						use_default);
		if (gdk_pixbuf_get_width(hint_white_pixmap) != grid_width
		    || gdk_pixbuf_get_height(hint_white_pixmap) != grid_height)
			default_pixmap_error("hint_white");
	}
	else {
		hint_black_pixmap = 0;
		hint_white_pixmap = 0;
	}

	if (show_last_move) {
		black_last_pixmap = load_pixmap("black_last", const_cast<const char**>(black_last_xpm), 
						use_default);
		if (gdk_pixbuf_get_width(black_last_pixmap) != grid_width
		    || gdk_pixbuf_get_height(black_last_pixmap) != grid_height)
			default_pixmap_error("black_last");

		white_last_pixmap = load_pixmap("white_last", const_cast<const char**>(white_last_xpm), 
						use_default);
		if (gdk_pixbuf_get_width(white_last_pixmap) != grid_width
		    || gdk_pixbuf_get_height(white_last_pixmap) != grid_height)
			default_pixmap_error("white_last");
	}
	else {
		black_last_pixmap = 0;
		white_last_pixmap = 0;
	}

				// Setup border pixmaps if required
	if (show_border) {
		left_pixmap = load_pixmap("left", const_cast<const char**>(left_xpm), 
					  use_default);
		left_margin = gdk_pixbuf_get_width(left_pixmap);
		if (gdk_pixbuf_get_height(left_pixmap) != grid_height * 8)
			default_pixmap_error("left");

		right_pixmap = load_pixmap("right", const_cast<const char**>(right_xpm), 
					   use_default);
		right_margin = gdk_pixbuf_get_width(right_pixmap);
		if (gdk_pixbuf_get_height(right_pixmap) != grid_height * 8)
			default_pixmap_error("right");

		top_pixmap = load_pixmap("top", const_cast<const char**>(top_xpm), 
					 use_default);
		top_margin = gdk_pixbuf_get_height(top_pixmap);
		if (gdk_pixbuf_get_width(top_pixmap) != grid_width * 8 + left_margin + right_margin)
			default_pixmap_error("top");

		bottom_pixmap = load_pixmap("bottom", const_cast<const char**>(bottom_xpm), 
					    use_default);
		bottom_margin = gdk_pixbuf_get_height(bottom_pixmap);
		if (gdk_pixbuf_get_width(bottom_pixmap) != grid_width * 8 + left_margin + right_margin)
			default_pixmap_error("bottom");
	}
	else {
		left_margin = 0;
		right_margin = 0;
		top_margin = 0;
		bottom_margin = 0;
	}

	GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(main_window));
	GtkSettings *settings = gtk_settings_get_for_screen(screen);
	gtk_icon_size_lookup_for_settings(settings, toolbar_icon_map[toolbar_icon_size], 
					  &icon_width, &icon_height);

	load_icon_image(icon_new, "icon_new", const_cast<const char**>(icon_new_xpm), 
			use_default);
	load_icon_image(icon_edit, "icon_edit", GTK_STOCK_FIND_AND_REPLACE, use_default);
	load_icon_image(icon_exit, "icon_exit", GTK_STOCK_QUIT, use_default);
	load_icon_image(icon_undo, "icon_undo", GTK_STOCK_UNDO, use_default);
	load_icon_image(icon_redo, "icon_redo", GTK_STOCK_REDO, use_default);

	load_icon_image(icon_begin, "icon_begin", GTK_STOCK_GOTO_TOP, use_default);
	load_icon_image(icon_prev, "icon_prev", GTK_STOCK_GO_UP, use_default);
	load_icon_image(icon_next, "icon_next", GTK_STOCK_GO_DOWN, use_default);
	load_icon_image(icon_end, "icon_end", GTK_STOCK_GOTO_BOTTOM, use_default);

	load_icon_image(icon_empty, "icon_empty", const_cast<const char**>(icon_empty_xpm), 
			use_default);
	load_icon_image(icon_black, "icon_black", const_cast<const char**>(icon_black_xpm), 
			use_default);
	load_icon_image(icon_white, "icon_white", const_cast<const char**>(icon_white_xpm), 
			use_default);
	load_icon_image(icon_bw, "icon_bw", const_cast<const char**>(icon_bw_xpm), 
			use_default);
	load_icon_image(icon_finish, "icon_finish", GTK_STOCK_APPLY, use_default);
}

void init_pixmaps()
{
	icon_new = gtk_image_new();
	icon_edit = gtk_image_new();
	icon_exit = gtk_image_new();
	icon_undo = gtk_image_new();
	icon_redo = gtk_image_new();

	icon_begin = gtk_image_new();
	icon_prev = gtk_image_new();
	icon_next = gtk_image_new();
	icon_end = gtk_image_new();

	icon_empty = gtk_image_new();
	icon_black = gtk_image_new();
	icon_white = gtk_image_new();
	icon_bw = gtk_image_new();
	icon_finish = gtk_image_new();

	if (theme_name.size()) {
		try {
			load_pixmaps(false);
		}
		catch (std::exception &e) {
			std::cout << std::flush;
			gtout(std::cerr, _("%$: %$\n")) << prog_name << e.what();
			gtout(std::cerr, _("%$: reverting to default internal theme\n"))
			     << prog_name;
			load_pixmaps(true);
		}
	}
	else
		load_pixmaps(true);
}

void resize_board_widget()
{
				// Set board size
	gtk_widget_set_size_request(board_widget,
				    8 * grid_width + left_margin + right_margin,
				    8 * grid_height + top_margin + bottom_margin);

				// Set main window size
				// Setting to 1x1 automatically set
				// window size to smallest possible
	gtk_window_resize(GTK_WINDOW(main_window), 1, 1);
}

/*************************************************************************
	Toolbar function
*************************************************************************/

GtkWidget *create_toolbar_button(GtkWidget *toolbar, GtkTooltips *tooltips, 
				 GtkWidget *icon, const char *label,
				 const char *tips,
				 void (*callback)(GtkWidget *, gpointer))
{
	GtkWidget *button = (GtkWidget *) gtk_tool_button_new(icon, label);
	gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(button), tooltips,
				  tips, NULL);
	g_signal_connect(G_OBJECT(button), "clicked",
			 G_CALLBACK(callback), 0);
	gtk_widget_show(icon);
	gtk_widget_show(button);
	gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
	return button;
}

GtkWidget *create_toolbar_radio_button(GtkWidget *toolbar, GtkTooltips *tooltips, 
				       GtkWidget *icon, const char *label,
				       const char *tips,
				       void (*callback)(GtkWidget *, gpointer),
				       GSList *list)
{
	GtkWidget *button = (GtkWidget *) gtk_radio_tool_button_new(list);
	gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(button), icon);
	gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), label);
	gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(button), tooltips,
				  tips, NULL);
	g_signal_connect(G_OBJECT(button), "clicked",
			 G_CALLBACK(callback), 0);
	gtk_widget_show(icon);
	gtk_widget_show(button);
	gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(button), -1);
	return button;
}

void create_toolbar()
{
	tooltips = gtk_tooltips_new();
	gtk_tooltips_enable(tooltips);

	main_toolbar = gtk_toolbar_new();
	gtk_toolbar_set_show_arrow(GTK_TOOLBAR(main_toolbar), FALSE);

	edit_toolbar = gtk_toolbar_new();
	gtk_toolbar_set_show_arrow(GTK_TOOLBAR(edit_toolbar), FALSE);

				// Main toolbar

	button_new = create_toolbar_button(main_toolbar, tooltips,
					   icon_new, _("New"),
					   _("Start a new game"),
					   menu_game_new);
	button_edit = create_toolbar_button(main_toolbar, tooltips,
					    icon_edit, _("Edit"),
					    _("Edit board"),
					    menu_game_edit_board);
	button_exit = create_toolbar_button(main_toolbar, tooltips,
					    icon_exit, _("Quit"),
					    _("Quit GRhino"),
					    menu_game_exit);

	GtkWidget *sep = (GtkWidget *) gtk_separator_tool_item_new ();
        gtk_widget_show (sep);
        gtk_toolbar_insert (GTK_TOOLBAR (main_toolbar), GTK_TOOL_ITEM(sep), -1);

		// Play mode
	button_undo = create_toolbar_button(main_toolbar, tooltips,
					    icon_undo, _("Undo"),
					    _("Undo move"),
					    menu_game_undo);
	button_redo = create_toolbar_button(main_toolbar, tooltips,
					    icon_redo, _("Redo"),
					    _("Redo move"),
					    menu_game_redo);

	sep = (GtkWidget *) gtk_separator_tool_item_new ();
        gtk_widget_show (sep);
        gtk_toolbar_insert (GTK_TOOLBAR (main_toolbar), GTK_TOOL_ITEM(sep), -1);

		// Play mode
	button_begin = create_toolbar_button(main_toolbar, tooltips,
					     icon_begin, _("Begin"),
					     _("Go to the game beginning"),
					     menu_game_begin);
	button_prev = create_toolbar_button(main_toolbar, tooltips,
					    icon_prev, _("Prev"),
					    _("Go to the previous move"),
					    menu_game_prev);
	button_next = create_toolbar_button(main_toolbar, tooltips,
					    icon_next, _("Next"),
					    _("Go to the next move"),
					    menu_game_next);
	button_end = create_toolbar_button(main_toolbar, tooltips,
					   icon_end, _("Last"),
					   _("Go to the last move"),
					   menu_game_end);

				// Edit toolbar

	button_empty = create_toolbar_radio_button(edit_toolbar, tooltips,
						   icon_empty, _("Empty"),
						   _("Fill with empty"),
						   menu_edit_empty,
						   NULL);
	GSList *group = gtk_radio_tool_button_get_group(GTK_RADIO_TOOL_BUTTON(button_empty));
	button_black = create_toolbar_radio_button(edit_toolbar, tooltips,
						   icon_black, _("Black"),
						   _("Fill with black"),
						   menu_edit_black, group);
	group = gtk_radio_tool_button_get_group(GTK_RADIO_TOOL_BUTTON(button_empty));
	button_white = create_toolbar_radio_button(edit_toolbar, tooltips,
						   icon_white, _("White"),
						   _("Fill with white"),
						   menu_edit_white, group);

	sep = (GtkWidget *) gtk_separator_tool_item_new ();
        gtk_widget_show (sep);
        gtk_toolbar_insert (GTK_TOOLBAR (edit_toolbar), GTK_TOOL_ITEM(sep), -1);

	button_bw = create_toolbar_button(edit_toolbar, tooltips,
					  icon_bw, _("To play"),
					  _("Player with first move"),
					  menu_edit_player);
	button_finish = create_toolbar_button(edit_toolbar, tooltips,
					      icon_finish, _("Finish"),
					      _("Start game with board"),
					      menu_edit_finish);
}

void menu_view_toolbar(GtkWidget * /*widget*/, gpointer /*data*/)
{
	show_toolbar = GTK_CHECK_MENU_ITEM (menu_tools_info[0].widget)->active;
	update_toolbar();
}

void menu_tools_evaluate_position(GtkWidget * /*widget*/, gpointer /*data*/)
{
	show_pattern_evaluation = GTK_CHECK_MENU_ITEM (menu_tools_info[2].widget)->active;
	evaluate_position();
}

void menu_tools_game_history(GtkWidget * /*widget*/, gpointer /*data*/)
{
	show_game_history = GTK_CHECK_MENU_ITEM (menu_tools_info[3].widget)->active;
	game_history();
}

void update_menu_and_toolbar(update_state_type /*u*/, void *)
{
	bool b = (view_mode == VIEW_MODE_NONE ? cur_game_info.is_undoable() : false);
	gtk_widget_set_sensitive(menu_edit_info[MENU_INDEX_UNDO].widget, b);
	gtk_widget_set_sensitive(button_undo, b);

	b = (view_mode == VIEW_MODE_NONE ? cur_game_info.is_redoable() : false);
	gtk_widget_set_sensitive(menu_edit_info[MENU_INDEX_REDO].widget, b);
	gtk_widget_set_sensitive(button_redo, b);

	b = (view_mode == VIEW_MODE_NONE && (cur_game_info.num_history > 1))
	     || (view_mode == VIEW_MODE_HISTORY && (view_position > 0));
	gtk_widget_set_sensitive(button_begin, b);
	gtk_widget_set_sensitive(menu_edit_info[MENU_INDEX_BEGIN].widget, b);
	gtk_widget_set_sensitive(button_prev, b);
	gtk_widget_set_sensitive(menu_edit_info[MENU_INDEX_PREV].widget, b);

	b = (view_mode == VIEW_MODE_HISTORY);
	gtk_widget_set_sensitive(button_next, b);
	gtk_widget_set_sensitive(menu_edit_info[MENU_INDEX_NEXT].widget, b);
	gtk_widget_set_sensitive(button_end, b);
	gtk_widget_set_sensitive(menu_edit_info[MENU_INDEX_END].widget, b);
}

// Called from Preferences dialog
void update_menu_and_toolbar_pref()
{
	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (menu_tools_info[MENU_INDEX_SHOW_TOOLBAR].widget),
				       show_toolbar);

	game_mode_type g = get_game_mode();
	if (g == COMPUTER_BOTH || g == COMPUTER_NONE)
		gtk_widget_set_sensitive(menu_settings_info[MENU_INDEX_SWITCH_AI].widget, 0);
	else			// One AI player modes
		gtk_widget_set_sensitive(menu_settings_info[MENU_INDEX_SWITCH_AI].widget, 1);

	gtk_toolbar_set_icon_size(GTK_TOOLBAR(main_toolbar), 
				  toolbar_icon_map[toolbar_icon_size]);
	gtk_toolbar_set_icon_size(GTK_TOOLBAR(edit_toolbar), 
				  toolbar_icon_map[toolbar_icon_size]);
}

/*************************************************************************
	Main function
*************************************************************************/

int main_real(gint argc, gchar *argv[])
{
	if (sizeof(int) < 4)
		throw std::out_of_range(_("cannot run on system with less than 32-bit int"));

	setlocale(LC_ALL, "");
	bindtextdomain("grhino", LOCALEDIR1);
	textdomain("grhino");
	bind_textdomain_codeset("grhino", "UTF-8");

	gnome_program_init("grhino", prog_ver, LIBGNOMEUI_MODULE, argc, argv,
			   GNOME_PROGRAM_STANDARD_PROPERTIES,
			   NULL);

	init_game_before_draw();

	gtk_window_set_default_icon_from_file(ICONDIR1 "/grhino.png", NULL);

	main_window = gnome_app_new("GRhino", _(prog_name));
	g_signal_connect(G_OBJECT(main_window), "destroy",
			 G_CALLBACK(destroy_signal), 0);
	gtk_widget_realize(main_window);

	gnome_app_create_menus(GNOME_APP(main_window), menu_main_info);
	init_pixmaps();

	board_widget = gtk_drawing_area_new();
				// Set board size
	gtk_widget_set_size_request(board_widget,
				    8 * grid_width + left_margin + right_margin,
				    8 * grid_height + top_margin + bottom_margin);

	g_signal_connect(G_OBJECT(board_widget), "expose_event",
			 G_CALLBACK(expose_signal), 0);
	g_signal_connect(G_OBJECT(board_widget), "button_press_event",
			 G_CALLBACK(button_press_signal), 0);
	g_signal_connect(G_OBJECT(board_widget), "button_release_event",
			 G_CALLBACK(button_press_signal), 0);
	g_signal_connect(G_OBJECT(board_widget), "motion_notify_event",
			 G_CALLBACK(mouse_move_signal), 0);
	gtk_widget_set_events(board_widget,
			      GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 
			      | GDK_BUTTON_MOTION_MASK | GDK_EXPOSURE_MASK);
	gtk_widget_show(board_widget);

	view_board_ptr = new byte_board_info();

	GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), board_widget, TRUE, TRUE, 0);
	gtk_widget_show(vbox);

	gnome_app_set_contents(GNOME_APP(main_window), vbox);

	status_widget = gnome_appbar_new(FALSE, TRUE, GNOME_PREFERENCES_USER);
	gnome_app_set_statusbar(GNOME_APP(main_window), status_widget);

	create_toolbar();
	if (show_toolbar) {
			// Only dock toolbar when it is visible.
			// This is to avoid broken window size calculation when
			// dock toolbar and immediately make it invisible.
		gnome_app_add_toolbar(GNOME_APP(main_window),
				      GTK_TOOLBAR(main_toolbar),
				      "main_toolbar",
				      BONOBO_DOCK_ITEM_BEH_EXCLUSIVE,
				      BONOBO_DOCK_TOP,
				      1, 0, 0);
		main_toolbar_docked = true;
	}
	update_menu_and_toolbar_pref();

	if (main_x != -1 && main_y != -1)
		gtk_window_move(GTK_WINDOW(main_window), main_x, main_y);
	gtk_widget_show(main_window);

	init_game();
	new_game();

			// Show other windows after game has
			// been properly setup
	if (show_pattern_evaluation)
		gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (menu_tools_info[2].widget),
					       show_pattern_evaluation);
	if (show_game_history)
		gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (menu_tools_info[3].widget),
					       show_game_history);
	gtk_window_present(GTK_WINDOW(main_window));

	gtk_main();

	return 0;
}

int main(gint argc, gchar *argv[])
{
	try {
		return main_real(argc, argv);
	}
	catch (std::exception &e) {
		std::cout << std::flush;
		gtout(std::cerr, _("%$: %$\n")) << prog_name << e.what();
	}
	catch (const char *s) {
		std::cout << std::flush;
		gtout(std::cerr, _("%$: exception %$\n")) << prog_name << s;
	}
	catch (...) {
		std::cout << std::flush;
		gtout(std::cerr, _("%$: unknown exception\n")) << prog_name;
	}
	return 1;
}
